package com.dosgi;

import java.io.File;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import java.util.TreeSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.dosgi.bean.BeanFactoryDefault;
import com.dosgi.bean.IBeanFactory;
import com.dosgi.classloader.ContextFinder;
import com.dosgi.clazz.ClassHandler;
import com.dosgi.kit.PathKit;
import com.dosgi.kit.StrKit;
import com.dosgi.kit.ThreadPoolKit;
import com.dosgi.kit.ValueKit;
import com.dosgi.module.IModuleContext;
import com.dosgi.module.ModuleContext;

/**
 * 默认应用上下文
 * 
 * @author dingnate
 *
 */
public class DosgiContextDefault implements IDosgiContext {
	private static transient final Logger LOG = LoggerFactory.getLogger(DosgiContextDefault.class);
	private LinkedList<IModuleContext> mContexts = new LinkedList<IModuleContext>();
	private IBeanFactory beanFactory = new BeanFactoryDefault();
	private HashMap<String, TreeSet<IModuleContext>> symbolicName2modules = new HashMap<String, TreeSet<IModuleContext>>();
	private HashMap<String, TreeSet<IModuleContext>> exportPackage2Modules = new HashMap<String, TreeSet<IModuleContext>>();
	private HashSet<ClassHandler> classHandlers = new HashSet<ClassHandler>();
	private String moduleHome;
	private Comparator<IModuleContext> mVersionComparator = new Comparator<IModuleContext>() {
		public int compare(IModuleContext o1, IModuleContext o2) {
			return StrKit.compareVersion(o2.getVersion(), o1.getVersion());
		}
	};

	public DosgiContextDefault(String moduleHome) {
		this.moduleHome = PathKit.resolvePath(ValueKit.getValue(moduleHome,
				System.getProperty(Dosgi.MODULE_HOME_KEY, System.getProperty("user.dir") + "/modules")));
	}

	/**
	 * 入口
	 * 
	 * @param args
	 * @param inst
	 */
	public void start(boolean standalone) {
		LOG.info("dosgi start...");
		// 检查模块主目录
		if (!new File(moduleHome).exists()) {
			LOG.error("dosgi start failed.\nCaused by module home:{} not exist", moduleHome);
			return;
		}
		try {
			// 获取模块
			scanModules();
			// 排序模块
			sortModules();
			// 初始化模块
			initModules();
			// 扫描模块类
			scanModuleClasses();
			// 启动模块
			if (standalone) {
				// 当前线程启动模块
				startModules();
				Dosgi.getLifeCycle().waitForClosed();
			} else {
				// 后台线程启动模块
				startModulesBackground();
			}
		} catch (Exception e) {
			LOG.error("dosgi start failed.", e);
		}
	}

	private void scanModuleClasses() {
		Dosgi.getLifeCycle().setState(LifeCycleState.SCANNING__CLASSES);
		LOG.info("start scan module classes...");
		for (IModuleContext mContext : mContexts) {
			ContextFinder contextFinder = new ContextFinder(mContext.getClassLoader());
			try {
				// 设置线程上下文类加载器
				contextFinder.setContextClassLoader();
				mContext.scanClasses();
				LOG.info("scan module classes {} success.", mContext.getSymbolicName());
			} catch (Exception e) {
				LOG.error("scan module classes " + mContext.getSymbolicName() + " failed.", e);
			} finally {
				// 设置线程上下文类加载器
				contextFinder.revertContextClassLoader();
			}
		}
		LOG.info("scan module classes success.");
		Dosgi.getLifeCycle().setState(LifeCycleState.SCANNED__CLASSES);
	}

	// 获取模块
	private void scanModules() {
		LOG.info("start get modules in module home:{} ...", moduleHome);
		File[] moduleFiles = new File(moduleHome).listFiles();
		for (File moduleFile : moduleFiles) {
			if (moduleFile.getName().endsWith(".jar")) {
				registerModule(getModuleContext(moduleFile));
			}
		}
	}

	/**
	 * @param moduleFile
	 * @return 
	 */
	private ModuleContext getModuleContext(File moduleFile) {
		try {
			ModuleContext mContext = new ModuleContext(moduleFile.getAbsolutePath());
			mContexts.add(mContext);
			LOG.info("get module from {} success.", moduleFile.getName());
			return mContext;
		} catch (Exception e) {
			LOG.error("error occured when get module from " + moduleFile.getName(), e);
			return null;
		}
	}

	/**
	 * 根据模块依赖关系排序，底层模块在前，上层模块在后
	 * 
	 * @param mContexts
	 */
	public void sortModules() {
		if (mContexts.isEmpty())
			return;
		HashSet<IModuleContext> passed = new HashSet<IModuleContext>();
		for (int i = 0; i < mContexts.size(); i++)
			pushRequiredForward(mContexts.get(i), mContexts, passed);
		//System模块放在第一个
		for (int i = 0; i < mContexts.size(); i++)
			if (Dosgi.PHASE_SYSTEM.equals(mContexts.get(i).getPhase())) {
				mContexts.addFirst(mContexts.remove(i));
				break;
			}
	}

	/**
	 * 把模块依赖的模块推到前面
	 * 
	 * @param mContext
	 * @param mContexts
	 * @param passed
	 */
	private static void pushRequiredForward(IModuleContext mContext, LinkedList<IModuleContext> mContexts,
			Collection<IModuleContext> passed) {
		if (passed.contains(mContext))
			return;
		passed.add(mContext);
		Collection<IModuleContext> resolvedRequireModulesWithImport = mContext.getResolvedRequireModulesWithImport();
		if (resolvedRequireModulesWithImport.isEmpty()) {
			if (mContexts.getFirst() == mContext)
				return;
			mContexts.remove(mContext);
			mContexts.addFirst(mContext);
			return;
		}
		int index = mContexts.indexOf(mContext);
		for (IModuleContext rMContext : resolvedRequireModulesWithImport) {
			int indexR = mContexts.indexOf(rMContext);
			if (indexR > index) {
				mContexts.remove(indexR);
				mContexts.add(index, rMContext);
			}
			pushRequiredForward(rMContext, mContexts, passed);
		}
	}

	// 初始化模块
	private void initModules() {
		Dosgi.getLifeCycle().setState(LifeCycleState.INITING);
		LOG.info("start init modules...");
		for (IModuleContext mContext : mContexts) {
			ContextFinder contextFinder = new ContextFinder(mContext.getClassLoader());
			try {
				// 设置线程上下文类加载器
				contextFinder.setContextClassLoader();
				mContext.init();
				LOG.info("init module {} success.", mContext.getSymbolicName());
			} catch (Exception e) {
				LOG.error("init module " + mContext.getSymbolicName() + " failed.", e);
			} finally {
				// 设置线程上下文类加载器
				contextFinder.revertContextClassLoader();
			}
		}
		LOG.info("init modules success.");
		Dosgi.getLifeCycle().setState(LifeCycleState.INITED);
	}

	// 后台启动模块
	private void startModulesBackground() {
		ThreadPoolKit.submit(new Runnable() {
			@Override
			public void run() {
				startModules();
			}
		});
	}

	// 启动模块
	private void startModules() {
		Dosgi.getLifeCycle().setState(LifeCycleState.STARTING);
		LOG.info("start modules...");
		for (IModuleContext mContext : mContexts) {
			ContextFinder contextFinder = new ContextFinder(mContext.getClassLoader());
			try {
				// 设置线程上下文类加载器
				contextFinder.setContextClassLoader();
				mContext.start();
				LOG.info("start module {} success.", mContext.getSymbolicName());
			} catch (Exception e) {
				LOG.error("start module " + mContext.getSymbolicName() + " failed.", e);
			} finally {
				// 设置线程上下文类加载器
				contextFinder.revertContextClassLoader();
			}
		}
		LOG.info("start modules success.");
		Dosgi.getLifeCycle().setState(LifeCycleState.STARTED);
	}

	/**
	 * @return 模块主目录
	 */
	public final String getModuleHome() {
		return moduleHome;
	}

	@Override
	public void start() {
		start(true);
	}

	@Override
	public IBeanFactory beanFactory() {
		return beanFactory;
	}

	@Override
	public void registerModule(IModuleContext mooduleContext) {
		// mapping symbolicName
		mappingBysymbolicName(mooduleContext);
		// mapping export packages
		mappingExportPackages(mooduleContext);
	}

	// @Override
	public IModuleContext getModule(String symbolicName, String version) {
		TreeSet<IModuleContext> set = symbolicName2modules.get(symbolicName);
		if (set == null || set.isEmpty())
			return null;
		if (StrKit.isBlank(version))
			return set.iterator().next();
		for (IModuleContext mContext : set) {
			if (version.equalsIgnoreCase(mContext.getVersion()))
				return mContext;
		}
		return null;
	}

	public IModuleContext getModuleByExportPackage(String packageName, String version) {
		TreeSet<IModuleContext> set = exportPackage2Modules.get(packageName);
		if (set == null || set.isEmpty())
			return null;
		if (StrKit.isBlank(version))
			return set.iterator().next();
		for (IModuleContext mContext : set) {
			if (version.equalsIgnoreCase(mContext.getVersion()))
				return mContext;
		}
		return null;
	}

	/**
	 * mapping export packages
	 * 
	 * @param mContext
	 */
	private void mappingExportPackages(IModuleContext mContext) {
		Set<String> exportPackages = mContext.getExportPackages();
		for (String exportPkg : exportPackages) {
			TreeSet<IModuleContext> set = exportPackage2Modules.get(exportPkg);
			if (set == null) {
				set = new TreeSet<IModuleContext>(mVersionComparator);
				exportPackage2Modules.put(exportPkg, set);
			}
			set.add(mContext);
		}
	}

	/**
	 * mapping symbolicName
	 * 
	 * @param mContext
	 */
	private void mappingBysymbolicName(IModuleContext mContext) {
		String symbolicName = mContext.getSymbolicName();
		TreeSet<IModuleContext> set = symbolicName2modules.get(symbolicName);
		if (set == null) {
			set = new TreeSet<IModuleContext>(mVersionComparator);
			symbolicName2modules.put(symbolicName, set);
		}
		set.add(mContext);
	}

	// @Override
	public void addClassHandler(ClassHandler classHandler) {
		classHandlers.add(classHandler);
	}

	public HashSet<ClassHandler> getClassHandlers() {
		return classHandlers;
	}

	@Override
	public void stop() {
		Collection<TreeSet<IModuleContext>> values = symbolicName2modules.values();
		for (TreeSet<IModuleContext> set : values) {
			for (IModuleContext mContext : set) {
				try {
					mContext.stop();
				} catch (Exception e) {
					LOG.error("stop module " + mContext.getSymbolicName() + " failed", e);
				}
			}
		}
	}

	@Override
	public void stopModule(String moduleJar) {
		ModuleContext mContext = getModuleContext(new File(moduleHome, moduleJar));
		IModuleContext module = getModule(mContext.getSymbolicName(), mContext.getVersion());
		if (module == null)
			return;
		stopModule(module.getSymbolicName(), module.getVersion());
	}

	@Override
	public void startModule(String moduleJar) {
		ModuleContext mContext = getModuleContext(new File(moduleHome, moduleJar));
		IModuleContext module = getModule(mContext.getSymbolicName(), mContext.getVersion());
		if (module == null) {
			module = mContext;
			registerModule(module);
		}
		startModule(module.getSymbolicName(), module.getVersion());
	}

	@Override
	public void startModule(String symbolicName, String version) {
		IModuleContext module = getModule(symbolicName, version);
		if (module == null)
			return;
		try {
			module.init();
			module.scanClasses();
			module.start();
			LOG.info("start module {} success.", module.getSymbolicName());
		} catch (Exception e) {
			LOG.error("start module " + module.getSymbolicName() + " failed.", e);
		}
	}

	@Override
	public void restartModule(String symbolicName, String version) {
		stopModule(symbolicName, version);
		startModule(symbolicName, version);
	}

	@Override
	public void stopModule(String symbolicName, String version) {
		IModuleContext module = getModule(symbolicName, version);
		if (module == null)
			return;
		try {
			module.stop();
			LOG.info("stop module {} success.", module.getSymbolicName());
		} catch (Exception e) {
			LOG.error("stop module " + module.getSymbolicName() + " failed.", e);
		}
	}
}
